package com.lancoo.ccas52.arrangecourse.clashesprocess;

import com.lancoo.ccas52.arrangecourse.common.BinaryUtil;
import com.lancoo.ccas52.arrangecourse.common.StrUtil;
import com.lancoo.ccas52.arrangecourse.entities.ClassHour;
import com.lancoo.ccas52.arrangecourse.entities.CourseUnit;
import com.lancoo.ccas52.arrangecourse.entities.TimeslotRoom;
import com.lancoo.ccas52.entity.ScheduleConflictInfo;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description 冲突记录
 * @Author Fortysun
 * @Date 2023/4/24 15:53
 **/
public class ClashesRecord extends ClashesRecordCompute {

    /**
     * 初始化冲突对象
     * @param conflictType 冲突类型，1:教学班冲突 2教室冲突（容量不足） 3教师冲突 4学生冲突 5未分配（没有分配教室课时）
     * @param teachingClassId 冲突的教学班id
     * @param conflictReason 冲突原因
     * @return
     */
    public ScheduleConflictInfo initConflictInfo(Integer taskId,
                                                 Integer conflictType,
                                                 Long teachingClassId,
                                                 Long roomId,
                                                 String timeslotCode,
                                                 Long clashTeachingClassId,
                                                 String clashWeeks,
                                                 String conflictReason) {
        ScheduleConflictInfo scheduleConflictInfo = new ScheduleConflictInfo();
        scheduleConflictInfo.setTaskId(taskId);
        scheduleConflictInfo.setConflictType(conflictType);
        scheduleConflictInfo.setTeachingClassId(teachingClassId);
        scheduleConflictInfo.setRoomId(roomId);
        scheduleConflictInfo.setTimeCode(timeslotCode);
        scheduleConflictInfo.setClashTeachingClassId(clashTeachingClassId);
        scheduleConflictInfo.setClashWeeks(clashWeeks);
        scheduleConflictInfo.setConflictReason(conflictReason);
        return scheduleConflictInfo;
    }

    /**
     * 计算教室容量冲突
     * @param classHourA 课时对象
     * @param classHourList 课时对象列表
     * @param clashesRecordList 冲突记录列表
     * @return
     */
    public List<ScheduleConflictInfo> recordClashes(CopyOnWriteArrayList<ClassHour> classHourList,
                                                    ClassHour classHourA,
                                                    List<ScheduleConflictInfo> clashesRecordList) {
        //如果这个课时，已经设置过了教室冲突、教师冲突以及学生冲突，那么跳过这个课时的比较
        //减少计算量，提高效率
        if (classHourA.isRoomClash()
                && classHourA.isTeacherClash()
                && classHourA.isStudentClash()) {
            return clashesRecordList;
        }

        for (ClassHour classHourB : classHourList) {
            //如果教室课时分组对象为空，说明此授课类型教学班没有分配教室课时分组，
            //那么就不存在冲突检测的问题
            if (classHourB.getTimeslotRoom() == null) {
                //直接跳过本次循环，进入下一次循环
                continue;
            }

            //不同的授课类型教学班，存在周次交叉的情况
            if (classHourA.getCourseUnit().getUnitId() != classHourB.getCourseUnit().getUnitId()
                    && isWeekClash(classHourA.getCourseUnit().getClassWeeks(), classHourB.getCourseUnit().getClassWeeks())) {

                //当前classHourA的冲突类型计算，都已完成，所以break掉，去统计下一个的
                if (isBreak(classHourA)) {
                    break;
                }
                else {
                    //统计教室冲突数
                    clashesRecordList = recordRoomClashes(classHourA, classHourB, clashesRecordList);
                }

                //当前classHourA的冲突类型计算，都已完成，所以break掉，去统计下一个的
                if (isBreak(classHourA)) {
                    break;
                }
                else {
                    //统计教师冲突数
                    clashesRecordList = recordTeacherClashes(classHourA, classHourB, clashesRecordList);
                }

                //当教学班不是在线选课类型的时候，才需要记录学生安排冲突
                if (classHourA.getCourseUnit().getTeachingClassType() != 1) {
                    //当前classHourA的冲突类型计算，都已完成，所以break掉，去统计下一个的
                    if (isBreak(classHourA)) {
                        break;
                    }
                    else {
                        //统计学生冲突数
                        clashesRecordList = recordStudentClashes(classHourA, classHourB, clashesRecordList);
                    }
                }
            }
        }

        return clashesRecordList;
    }

    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @return
     */
    protected List<ScheduleConflictInfo> addRoomClashesToList(ClassHour classHourA,
                                                            ClassHour classHourB,
                                                            List<ScheduleConflictInfo> clashesRecordList) {
        //当前排课单元的，教学班单元信息；
        CourseUnit curCourseUnit = classHourA.getCourseUnit();
        //与当前排课单元，冲突的， 排课单元的教学班单元信息；
        CourseUnit clashCourseUnit = classHourB.getCourseUnit();

        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();

        //计算发生冲突的周次字符串
        String clashWeeks = BinaryUtil.binaryAnd(curTimeslotRoom.getWeeks(), clashTimeslotRoom.getWeeks());

        //记录教室冲突，同一教室，同一时间，安排了两个不同的上课单元，即CourseUnit
        clashesRecordList.add(initConflictInfo(curCourseUnit.getTaskId(),
                1,
                curCourseUnit.getTeachingClassId(),
                curTimeslotRoom.getRoomId(),
                curTimeslotRoom.getTimeslotCode(),
                clashCourseUnit.getTeachingClassId(),
                clashWeeks,
                "教室安排冲突--当前教室："+curTimeslotRoom.getRoomName()+
                        "，在" + curTimeslotRoom.getTimeslotName()+
                        "同时安排了教学班："+ curCourseUnit.getTeachingClassName()
                        + "的周课时单元：" + curCourseUnit.getUnitId()
                        + "和教学班："+ clashCourseUnit.getTeachingClassName()
                        + "的周课时单元：" + clashCourseUnit.getUnitId()));

        return clashesRecordList;
    }

    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @return
     */
    private List<ScheduleConflictInfo> recordRoomClashes(ClassHour classHourA,
                                                      ClassHour classHourB,
                                                      List<ScheduleConflictInfo> clashesRecordList) {

        //如果已标记，说明这种类型的冲突已经统计过，那么不再统计
        if (classHourA.isRoomClash()) {
            return clashesRecordList;
        }

        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();

        //不同的授课类型教学班，存在周次交叉的情况，并且对应的教室课时id相同，则教室、课时安排冲突
        if (curTimeslotRoom.getTimeslotRoomId() == clashTimeslotRoom.getTimeslotRoomId()) {
            //将A的教室冲突标记设为true，下次就不需要再比较教室冲突了
            classHourA.setRoomClash(true);

            //将classHourA的冲突记录到冲突列表
            clashesRecordList = addRoomClashesToList(classHourA, classHourB, clashesRecordList);
            //将B的教室冲突标记设置true，下次循环遍历到B就不需要再比较教室冲突了
            classHourB.setRoomClash(true);
            //因为A和B互相冲突，将classHourB的冲突记录到冲突列表,下次遍历到B的时候，就不需要再记录了
            clashesRecordList = addRoomClashesToList(classHourB, classHourA, clashesRecordList);
            //System.out.println("教室冲突");
            return clashesRecordList;
        }

        return clashesRecordList;
    }

    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @param clashesStudentId 冲突的学生id
     * @return
     */
    protected List<ScheduleConflictInfo> addStudentClashesToList(ClassHour classHourA,
                                                            ClassHour classHourB,
                                                            List<ScheduleConflictInfo> clashesRecordList,
                                                               String clashesStudentId) {
        //当前排课单元的，教学班单元信息；
        CourseUnit curCourseUnit = classHourA.getCourseUnit();
        //与当前排课单元，冲突的， 排课单元的教学班单元信息；
        CourseUnit clashCourseUnit = classHourB.getCourseUnit();
        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();

        //计算发生冲突的周次字符串
        String clashWeeks = BinaryUtil.binaryAnd(curTimeslotRoom.getWeeks(), clashTimeslotRoom.getWeeks());

        //记录教师冲突，同一时间，安排了同一个老师
        clashesRecordList.add(initConflictInfo(curCourseUnit.getTaskId(),
                4,
                curCourseUnit.getTeachingClassId(),
                curTimeslotRoom.getRoomId(),
                curTimeslotRoom.getTimeslotCode(),
                clashCourseUnit.getTeachingClassId(),
                clashWeeks,
                "学生安排冲突--当前教学班："+ curCourseUnit.getTeachingClassName()
                        + "与教学班：" + clashCourseUnit.getTeachingClassName()
                        + "在相同的时间上课，但是他们都包括学生："+ clashesStudentId));


        return clashesRecordList;
    }
    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @return
     */
    private List<ScheduleConflictInfo> recordStudentClashes(ClassHour classHourA,
                                                         ClassHour classHourB,
                                                         List<ScheduleConflictInfo> clashesRecordList) {
        //如果已标记，说明这种类型的冲突已经统计过，那么不再统计
        if (classHourA.isStudentClash()) {
            return clashesRecordList;
        }

        //计算冲突的学生的id
        String clashesStudentId = StrUtil.clashString(classHourA.getCourseUnit().getStudentIds(),
                classHourB.getCourseUnit().getStudentIds());
        //相同的课时，安排了相同的教师，那么教师冲突
        if (!StringUtils.isBlank(clashesStudentId)) {
            //将A的学生冲突标记设为true，下次就不需要再比较学生冲突了
            classHourA.setStudentClash(true);
            //将classHourA的冲突记录到冲突列表
            clashesRecordList = addStudentClashesToList(classHourA, classHourB, clashesRecordList, clashesStudentId);

            //将B的学生冲突标记设置true，下次循环遍历到B，就不需要再比较学生冲突了
            classHourB.setStudentClash(true);
            //因为A和B互相冲突，将classHourB的冲突记录到冲突列表,下次遍历到B的时候，就不需要再记录了
            clashesRecordList = addStudentClashesToList(classHourA, classHourB, clashesRecordList, clashesStudentId);
            //System.out.println("学生冲突");
            return clashesRecordList;
        }

        return clashesRecordList;
    }

    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @param clashesTeacherId 冲突教师id
     * @return
     */
    protected List<ScheduleConflictInfo> addTeacherClashesToList(ClassHour classHourA,
                                                               ClassHour classHourB,
                                                               List<ScheduleConflictInfo> clashesRecordList,
                                                               String clashesTeacherId) {
        //当前排课单元的，教学班单元信息；
        CourseUnit curCourseUnit = classHourA.getCourseUnit();
        //与当前排课单元，冲突的， 排课单元的教学班单元信息；
        CourseUnit clashCourseUnit = classHourB.getCourseUnit();

        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();

        //计算发生冲突的周次字符串
        String clashWeeks = BinaryUtil.binaryAnd(curTimeslotRoom.getWeeks(), clashTimeslotRoom.getWeeks());

        //记录教师冲突，同一时间，安排了同一个老师
        clashesRecordList.add(initConflictInfo(curCourseUnit.getTaskId(),
                3,
                curCourseUnit.getTeachingClassId(),
                curTimeslotRoom.getRoomId(),
                curTimeslotRoom.getTimeslotCode(),
                clashCourseUnit.getTeachingClassId(),
                clashWeeks,
                "教师安排冲突--当前教学班："+ curCourseUnit.getTeachingClassName()
                        + "与教学班：" + clashCourseUnit.getTeachingClassName()
                        + "在相同的时间上课，但是他们的主讲教师都包括："+ clashesTeacherId));

        return clashesRecordList;
    }

    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param clashesRecordList  冲突记录列表
     * @return
     */
    private List<ScheduleConflictInfo> recordTeacherClashes(ClassHour classHourA,
                                                         ClassHour classHourB,
                                                         List<ScheduleConflictInfo> clashesRecordList) {
//        //不同院系的课程，不需要判断教师冲突
//        if (!classHourA.getCourseUnit().getCollegeId().equals(classHourB.getCourseUnit().getCollegeId())) {
//            return clashesRecordList;
//        }
        //如果已标记，说明这种类型的冲突已经统计过，那么不再统计
        if (classHourA.isTeacherClash()) {
            return clashesRecordList;
        }

        //如果有一个教学班排课单元没有设置教师信息,不计算教师冲突
        if (StringUtils.isBlank(classHourA.getCourseUnit().getTeacherIds())
                || StringUtils.isBlank(classHourB.getCourseUnit().getTeacherIds())) {
            return clashesRecordList;
        }

        //计算冲突的教师的id
        String clashesTeacherId = StrUtil.clashString(classHourA.getCourseUnit().getTeacherIds(),
                classHourB.getCourseUnit().getTeacherIds());
        //相同的课时，安排了相同的教师，那么教师冲突
        if (!StringUtils.isBlank(clashesTeacherId)) {
            //将A的教师冲突标记设为true，下次就不需要再比较学生冲突了
            classHourA.setTeacherClash(true);
            //将classHourA的冲突记录到冲突列表
            clashesRecordList = addTeacherClashesToList(classHourA, classHourB, clashesRecordList, clashesTeacherId);
            //将B的教师冲突标记设置true，下次遍历到B的时候，就不需要再记录冲突了
            classHourB.setTeacherClash(true);
            //因为A和B互相冲突，将classHourB的冲突记录到冲突列表,下次遍历到B的时候，就不需要再记录了
            clashesRecordList = addTeacherClashesToList(classHourB, classHourA, clashesRecordList, clashesTeacherId);

            //System.out.println("教师冲突");

            return clashesRecordList;
        }
        return clashesRecordList;
    }

}
